#ifndef _JADRO_H
#define _JADRO_H

//#include <mem.h>
#include <string.h>
#include <stdio.h>
//#include <conio.h>
#include "lex_an.h"
#include "pamet.h"
#include "unikat.h"
#include "q_io.h"

 //pridal OA
#define _Sdisjunkce  1
#define _Skonjunkce  2
#define _Snegace     3
#define _Srovnost    4
#define _Snerovnost  5
#define _Sdans       6
#define _Shors       7

#define stricmp strcmp

//extern int alok;

class Qstrom;

class Prom
{
    protected:
      char* jmeno; Prom* dal;
    public:
      Prom(char* j,Prom* d):jmeno(j),dal(d) {}
      ~Prom() { if(dal) delete dal; }
      int existuje(char* j)
      { if(!j && !jmeno || !stricmp(j,jmeno)) return 1;
	else if(dal) return dal->existuje(j); else return 0; }
      void vypis()
      { if(!jmeno) QERR<<"null ; "; else QERR<<jmeno<<" ; ";
	if(dal) dal->vypis(); }
};

class Retezec
{
  protected:
    int poc;
    char* obsah;
    Retezec *dal,*zpet;
  public:
    Retezec(char* o)
      { obsah=new char[strlen(o)+1];
        malopameti(obsah);
	strcpy(obsah,o); poc=1;
	dal=zpet=NULL; }
    ~Retezec();
    void zrus() { if(!--poc) delete this; }
    void zvys() { poc++; }
    char* vobsah() { return obsah; }
    int vpoc() { return poc; }
    void npoc(int n) { poc=n; } //pridano kvuli nacitani ze souboru (OA)
    Retezec *dalsi() { return dal; }
    Retezec *predchozi() { return zpet; }
    void ndal(Retezec *r) { dal=r; }
    void nzpet(Retezec *r) { zpet=r; }
};

Retezec* kretezec(char*);

void vypisretezcu();

class Qstrom
{
  private:
    Qstrom *syn,*bratr,*dalsi;
    //char* obsah;
    Retezec* jmeno;
    int konst,poc;
    //long lid;
    Qstrom *desint;
  public:
    Qstrom(char* o,int k)
     { //obsah=new char[strlen(o)+1]; //alok++;
       //strcpy(obsah,o);
       if(o) jmeno=kretezec(o); else jmeno=NULL;
       syn=bratr=dalsi=NULL;
       konst=k; poc=1; desint=NULL; }
    Qstrom(Retezec* r,int k)
     { //obsah=new char[strlen(o)+1]; //alok++;
       //strcpy(obsah,o);
       jmeno=r;
       syn=bratr=dalsi=NULL;
       konst=k; poc=1; desint=NULL; }
    Qstrom(Qstrom* a);
    ~Qstrom();
    int kontrola(Prom*,Lseznam*);
    int nul() { if(konst==_Lnul) return 1; else return 0; }
    int promenna() { if(konst==_Latomovapromenna ||
			konst==_Lstromovapromenna ||
			konst==_Lseznamovapromenna) return konst; else return 0; }
    //long vlid() { return lid; }
    //void nlid(long l) { lid=l; }
    void pocplus() { poc++; }
    void pocminus() { if(!--poc) delete this; }
    int pocitadlo() { return poc; }
    void npoc(int p) { poc=p; } //pridano kvuli nacitani ze souboru (OA)
    void test(FILE*,int=_Lseznamovapromenna,int=-1);
    //void test2(FILE*,int=1);
    void test3(FILE*);
    //void test4(FILE*);
    int neprazdny() { return (jmeno && jmeno->vobsah() || desint ? 1 : 0); }
    Qstrom* vsyn() { return syn; }
    Qstrom* vbratr() { return bratr; }
    Qstrom* vdesint() { return desint; }
    Qstrom* adsyn() { if(syn)syn->pocplus();return syn; }
    Qstrom* adbratr() { if(bratr)bratr->pocplus();return bratr; }
    Qstrom* addesint() { if(desint)desint->pocplus();return desint; }
    Qstrom* dal() { return dalsi; }
    char* vobsah() { if(jmeno) return jmeno->vobsah(); else return NULL; }
    int vkonst() { return konst; }
    Retezec* vjmeno() { return jmeno; } //pridano kvuli ukladani do souboru (OA)
    void ndesint(Qstrom* t) { desint=t; }
    void nsyn(Qstrom* t) { syn=t; }
    void nbratr(Qstrom* t) { bratr=t; }
    void ndalsi(Qstrom* d) { dalsi=d; }
    void nobsah(char* o)
     { if(jmeno)jmeno->zrus();
       jmeno=new Retezec(o); }
    void nkonst(int k) { konst=k; }
    void njmeno(Retezec *j) { jmeno=j; } //pridano kvuli nacitani ze souboru (OA)
	void exportXml(FILE*);
	void testXml(FILE*);
};

class Qhrana
{
  private:
    Qhrana *retez1,*retez2,*vetev1,*vetev2,*dalsi;
    Qstrom *strom;
    int poc,pouz;
    unsigned long urov;
    //long lid;
  public:
    Qhrana(Qstrom* t)
     { strom=t;
       retez1=retez2=vetev1=vetev2=dalsi=NULL;
       poc=1; pouz=urov=0; }
    ~Qhrana();
    int projit(int, int, FILE*);
    int kontrola(Prom*,Lseznam*);
    //long vlid() { return lid; }
    //void nlid(long l) { lid=l; }
    int vpouz() { return pouz; }
    void npouz(int p) { pouz=p; }
    unsigned long vurov() { return urov; }
    void nurov(unsigned long u) { urov=u; }
    void pocplus() { poc++; }
    void pocminus() { if(!--poc) delete this; }
    int pocitadlo() { return poc; }
    void npoc(int p) { poc=p; } //pridano kvuli nacitani ze souboru (OA)
    void test(FILE*);
    void test2(FILE*,int=1,int=0);
    //void test3(FILE*);
    void test4(FILE*);
    Qhrana* vretez1() { return retez1; }
    Qhrana* vretez2() { return retez2; }
    Qhrana* vvetev1() { return vetev1; }
    Qhrana* vvetev2() { return vetev2; }
    Qstrom* adstrom() { if(strom)strom->pocplus();return strom; }
    Qhrana* adretez1() { if(retez1)retez1->pocplus();return retez1; }
    Qhrana* adretez2() { if(retez2)retez2->pocplus();return retez2; }
    Qhrana* advetev1() { if(vetev1)vetev1->pocplus();return vetev1; }
    Qhrana* advetev2() { if(vetev2)vetev2->pocplus();return vetev2; }
    Qhrana* dal() { return dalsi; }
    Qstrom* vstrom() { return strom; }
    void nretez1(Qhrana* u) { retez1=u; }
    void nretez2(Qhrana* u) { retez2=u; }
    void nvetev1(Qhrana* u) { vetev1=u; }
    void nvetev2(Qhrana* u) { vetev2=u; }
    void ndalsi(Qhrana* d) { dalsi=d; }
    void nstrom(Qstrom* t) { strom=t; }
	void exportXml(FILE*);
};

class Qvstup
{
  protected:
    Qhrana* hrana;
    Qvstup* dal;
    Ustrom* unik;
	void exportXml2(FILE*);
  public:
    Qvstup(Qhrana* h,Qvstup* d,int un)
    { hrana=h; dal=d;
      if(un) {unik=new Ustrom; malopameti(unik);}
      else unik=NULL; }
    Qvstup(FILE*,int=0); // nacte nove grafy ze souboru (viz struktio.cpp)
    ~Qvstup() { if(hrana) hrana->pocminus(); if(dal) delete dal; if(unik) delete unik; }
    Qhrana* vhrana() { return hrana; }
    void nhrana(Qhrana* h) { hrana=h; }
    Qvstup* dalsi() { return dal; }
    void ndalsi(Qvstup* d) { dal=d; }
    void test1(FILE*); // vystup na obrazovky - vypise vsechny kombinace
    void test2(FILE*); // vystup do souboru - vypisuje po usecich
    void uloz(FILE*); // ulozi grafy do souboru (viz struktio.cpp)
    void nacti(FILE*); // nacte grafy ze souboru (viz struktio.cpp)
    Ustrom *vunik() { return unik; }
    void nunik(Ustrom *u) { unik=u; }
	void exportXml(FILE*);
};

class Uzel
{
    protected:
      Qstrom* qs;
      Qhrana* qh;
      Uzel* dal;
    public:
      Qstrom* vstr() { if(qs) return qs; else return qh->vstrom(); }
      Qhrana* vhrn() { return qh; }
      Uzel(Qstrom* s,Qhrana* h,Uzel* d):qs(s),qh(h),dal(d) {}
      Uzel* vdal() { return dal; }
};

/*class Qdesintegrator:public Qhrana
{
  protected:
    Qhrana* u;
  public:
    Qdesintegrator(Qhrana* uu) { u=uu; }
    ~Qdesintegrator() { if(u) delete u; }
    Qhrana* uzel() { return u; }
    void test(FILE*) { printf("$$"); u->test(); }
};/**/

 //pridal OA
#include "bpromsez.h"

class Qpodminka
{
  protected:
    int poc;
  public:
    virtual void test(FILE*)=0;
    virtual ~Qpodminka() {};
    Qpodminka() { poc=0; }
    void pocplus() { poc++; }
    void smazat() { if(!poc) delete this; else poc--; }
    virtual int typ()=0;
    virtual void obsah(Qpodminka *&podm1,Qpodminka *&podm2)=0;
    virtual void obsah(Qstrom *&t1,Qstrom *&t2)=0;
    virtual int over(Bpromsez *ps)=0;
    virtual int kontrola(Prom*,Lseznam*)=0;
};

class Qdisjunkce:public Qpodminka
{
  protected:
    Qpodminka *p1,*p2;
  public:
    Qdisjunkce(Qpodminka* q1,Qpodminka* q2) { p1=q1; p2=q2; }
    ~Qdisjunkce() { if(p1) p1->smazat(); if(p2) p2->smazat(); }
    virtual void test(FILE*);
    virtual int typ() { return _Sdisjunkce; }
    virtual void obsah(Qpodminka *&podm1,Qpodminka *&podm2)
      { podm1=p1; podm2=p2; }
    virtual void obsah(Qstrom *&t1,Qstrom *&t2)
      { t1=NULL; t2=NULL; }
    virtual int over(Bpromsez *ps);
    virtual int kontrola(Prom* pr,Lseznam* ls)
     { if(!p1->kontrola(pr,ls) || !p2->kontrola(pr,ls)) return 0; else return 1; }
};

class Qkonjunkce:public Qpodminka
{
  protected:
    Qpodminka *p1,*p2;
  public:
    Qkonjunkce(Qpodminka* q1,Qpodminka* q2) { p1=q1; p2=q2; }
    ~Qkonjunkce() { if(p1) p1->smazat(); if(p2) p2->smazat(); }
    virtual void test(FILE*);
    virtual int typ() { return _Skonjunkce; }
    virtual void obsah(Qpodminka *&podm1,Qpodminka *&podm2)
      { podm1=p1; podm2=p2; }
    virtual void obsah(Qstrom *&t1,Qstrom *&t2)
      { t1=NULL; t2=NULL; }
    virtual int over(Bpromsez *ps);
    virtual int kontrola(Prom* pr,Lseznam* ls)
     { if(!p1->kontrola(pr,ls) || !p2->kontrola(pr,ls)) return 0; else return 1; }
};

class Qnegace:public Qpodminka
{
  protected:
    Qpodminka *p;
  public:
    Qnegace(Qpodminka* q) { p=q;}
    ~Qnegace() { if(p) p->smazat(); }
    virtual void test(FILE*);
    virtual int typ() { return _Snegace; }
    virtual void obsah(Qpodminka *&podm1,Qpodminka *&podm2)
      { podm1=p; podm2=NULL; }
    virtual void obsah(Qstrom *&t1,Qstrom *&t2)
      { t1=NULL; t2=NULL; }
    virtual int over(Bpromsez *ps);
    virtual int kontrola(Prom* pr,Lseznam* ls)
     { return p->kontrola(pr,ls); }
};

class Qrovnost:public Qpodminka
{
  protected:
    Qstrom *u1,*u2;
  public:
    Qrovnost(Qstrom* q1,Qstrom* q2) { u1=q1; u2=q2; }
    ~Qrovnost() { if(u1) u1->pocminus(); if(u2) u2->pocminus(); }
    virtual void test(FILE*);
    virtual int typ() { return _Srovnost; }
    virtual void obsah(Qpodminka *&podm1,Qpodminka *&podm2)
      { podm1=NULL; podm2=NULL; }
    virtual void obsah(Qstrom *&t1,Qstrom *&t2)
      { t1=u1; t2=u2; }
    virtual int over(Bpromsez *ps);
    virtual int kontrola(Prom* pr,Lseznam* ls)
     { if(!u1->kontrola(pr,ls) || !u2->kontrola(pr,ls)) return 0; else return 1; }
};

class Qnerovnost:public Qpodminka
{
  protected:
    Qstrom *u1,*u2;
  public:
    Qnerovnost(Qstrom* q1,Qstrom* q2) { u1=q1; u2=q2; }
    ~Qnerovnost() { if(u1) u1->pocminus(); if(u2) u2->pocminus(); }
    virtual void test(FILE*);
    virtual int typ() { return _Snerovnost; }
    virtual void obsah(Qpodminka *&podm1,Qpodminka *&podm2)
      { podm1=NULL; podm2=NULL; }
    virtual void obsah(Qstrom *&t1,Qstrom *&t2)
      { t1=u1; t2=u2; }
    virtual int over(Bpromsez *ps);
    virtual int kontrola(Prom* pr,Lseznam* ls)
     { if(!u1->kontrola(pr,ls) || !u2->kontrola(pr,ls)) return 0; else return 1; }
};

class Qdans:public Qpodminka
{
  protected:
    Qstrom *u1,*u2;
  public:
    Qdans(Qstrom* q1,Qstrom* q2) { u1=q1; u2=q2; }
    ~Qdans() { if(u1) u1->pocminus(); if(u2) u2->pocminus(); }
    virtual void test(FILE*);
    virtual int typ() { return _Sdans; }
    virtual void obsah(Qpodminka *&podm1,Qpodminka *&podm2)
      { podm1=NULL; podm2=NULL; }
    virtual void obsah(Qstrom *&t1,Qstrom *&t2)
      { t1=u1; t2=u2; }
    virtual int over(Bpromsez *ps);
    virtual int kontrola(Prom* pr,Lseznam* ls)
     { if(!u1->kontrola(pr,ls) || !u2->kontrola(pr,ls)) return 0; else return 1; }
};

class Qhors:public Qpodminka
{
  protected:
    Qstrom *u1,*u2;
  public:
    Qhors(Qstrom* q1,Qstrom* q2) { u1=q1; u2=q2; }
    ~Qhors() { if(u1) u1->pocminus(); if(u2) u2->pocminus(); }
    virtual void test(FILE*);
    virtual int typ() { return _Shors; }
    virtual void obsah(Qpodminka *&podm1,Qpodminka *&podm2)
      { podm1=NULL; podm2=NULL; }
    virtual void obsah(Qstrom *&t1,Qstrom *&t2)
      { t1=u1; t2=u2; }
    virtual int over(Bpromsez *ps);
    virtual int kontrola(Prom* pr,Lseznam* ls)
     { if(!u1->kontrola(pr,ls) || !u2->kontrola(pr,ls)) return 0; else return 1; }
};

class Qpravidlo
{
  protected:
    Qhrana *lretez,*pretez;
    Qpodminka* podm;
    Qpravidlo* dalsi;
    int _id,fid;
  public:
    char* soubor; // jmeno souboru, ve kterem je pravidlo definovano
    unsigned long rad1,slp1,rad2,slp2; // cislo radku a sloupce, kde pravidlo zacina, resp. konci
    Qpravidlo(Qhrana* lr,Qhrana* pr,Qpodminka* po) { lretez=lr; pretez=pr; podm=po; dalsi=NULL; soubor=NULL; }
    Qpravidlo(Qhrana* lr,Qhrana* pr,Qpodminka* po,char* soub,unsigned long r1,unsigned long s1,unsigned long r2,unsigned long s2)
    { lretez=lr; pretez=pr; podm=po; dalsi=NULL;
      rad1=r1; slp1=s1; rad2=r2; slp2=s2;
		if (soub == NULL) soubor == NULL;
		else {
			soubor=new char[strlen(soub)+1];
			strcpy(soubor,soub);
		} }
    Qpravidlo(Qhrana* lr,Qhrana* pr) { lretez=lr; pretez=pr; podm=NULL; dalsi=NULL; soubor=NULL; }
    ~Qpravidlo()
     {
       /* puts("! >>> DELETING pravidlo <<< !"); test(); getch(); /**/
       if(lretez) lretez->pocminus();//upravil OA
       if(pretez) pretez->pocminus();//upravil OA
       //if(podm) if(podm->pocitadlo()==1) delete podm; else podm->pocminus();
       if(podm) podm->smazat();
       if(dalsi) delete dalsi;
       if(soubor) delete soubor;
     }
    Qhrana* levyretez() { return lretez; }
    Qhrana* pravyretez() { return pretez; }
    Qpodminka* podminka() { return podm; }
    Qpravidlo* dal() { return dalsi; }
    void nastav(Qpravidlo* p) { dalsi=p; }
    void test(FILE*);
    int vid() { return _id; }
    void nid(int n) { _id=n; }
    int vfid() { return fid; }
    void nfid(int n) { fid=n; }
    void ocislovat(int n,int f)
     { nid(n); nfid(f); if(dal()) dal()->ocislovat(n+1,f); }
};

class Qfaze
{
  protected:
    Qpravidlo* prav;
    Qfaze* dalsi;
    int _id;
  public:
    Qfaze(Qpravidlo* p) { prav=p; dalsi=NULL; }
    ~Qfaze() { if(prav) delete prav; if(dalsi) delete dalsi; }
    Qfaze* dal() { return dalsi; }
    void nastav(Qfaze* f) { dalsi=f; }
    void test(FILE*);
    Qpravidlo* pravidlo() { return prav; }
    int vid() { return _id; }
    void nid(int n) { _id=n; }
    Qpravidlo* pravidloid(int);
    void ocislovat(int n)
     { nid(n); pravidlo()->ocislovat(1,n);
       if(dal()) dal()->ocislovat(n+1); }
    Qpravidlo* edpravidlo(char*,unsigned long,unsigned long); // vrati ukazatel na pravidlo ve specifikovanem souboru na danem radku a sloupci
};

class Qprogram
{
  protected:
    Qfaze* _faze;
  public:
    Qprogram(Qfaze* f) { _faze=f; }
    Qprogram(FILE*); //nacte novy program ze souboru (viz struktio.cpp)
    ~Qprogram() { if(_faze) delete _faze; }
    Qfaze* faze() { return _faze; }
    void nfaze(Qfaze *f) { _faze=f; }
    void test(FILE*);
    Qfaze* fazeid(int);
    Qpravidlo* pravidloid(int f,int p)
     { Qfaze* qf=fazeid(f);
       if(qf) return qf->pravidloid(p); else return NULL; }
    void ocislovat() { faze()->ocislovat(1); }
    void uloz(FILE*); //ulozi program do souboru (viz struktio.cpp)
    void nacti(FILE*); //nacte program ze souboru (viz struktio.cpp)
    int beh(Qvstup *vst,FILE *f); //provede vypocet na vstupu vst s logem do fajlu f (f==NULL znaci zadny log)
    void pripoj(Qprogram *prg); //faze prgu pripoji za sve faze a smaze prg
    Qpravidlo* edpravidlo(char*,unsigned long,unsigned long); // vrati ukazatel na pravidlo ve specifikovanem souboru na danem radku a sloupci
};

int test_pravidla(Qhrana*,Qhrana*,Qpodminka*,Lseznam*);

#endif
